#if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2014 Audiokinetic Inc. / All Rights Reserved
//
//////////////////////////////////////////////////////////////////////

using UnityEngine;
using System.Collections.Generic;
using System;

/// @brief Maintains the list of loaded SoundBanks loaded. This is currently used only with AkAmbient objects.
public static class AkBankManager
{
	class BankHandle
	{
		protected int m_RefCount = 0;
		protected uint m_BankID;
		protected float m_beginUnloadTime = 0.0f;

		public string bankName;

		public BankHandle(string name)
		{
			bankName = name;
		}

		public int RefCount
		{
			get
			{
				return m_RefCount;
			}
		}

		public float BeginUnloadTime
		{
			get
			{
				return m_beginUnloadTime;
			}
			set
			{
				m_beginUnloadTime = value;
			}
		}

		override public bool Equals(System.Object obj)
		{
			BankHandle handle = (BankHandle) obj;
			return this.bankName.Equals(handle.bankName);
		}

		override public int GetHashCode()
		{
			return this.bankName.GetHashCode();
		}

		/// Loads a bank. This version blocks until the bank is loaded. See AK::SoundEngine::LoadBank for more information.
		public virtual AKRESULT DoLoadBank()
		{
			return AkSoundEngine.LoadBank(bankName, AkSoundEngine.AK_DEFAULT_POOL_ID, out m_BankID);
		}

		public void LoadBank()
		{
			if (m_RefCount == 0)
			{
				AKRESULT res = DoLoadBank();
				LogLoadResult(res);
			}

			IncRef();
		}

		/// Unloads a bank.
		public virtual void UnloadBank()
		{
			AkSoundEngine.UnloadBank(m_BankID, IntPtr.Zero, null, null);
		}

		public void IncRef()
		{
			m_RefCount++;
		}

		public void DecRef()
		{
			m_RefCount--;
		}

		protected void LogLoadResult(AKRESULT result)
		{
			if (result != AKRESULT.AK_Success)
				AkSoundEngine.LogWarning("WwiseUnity: Bank " + bankName + " failed to load (" + result.ToString() + ")");
		}
	}

	class AsyncBankHandle : BankHandle
	{
		AkCallbackManager.BankCallback bankCallback = null;

		public AsyncBankHandle(string name, AkCallbackManager.BankCallback callback) : base(name)
		{
			bankCallback = callback;
		}

		static void GlobalBankCallback(uint in_bankID, IntPtr in_pInMemoryBankPtr, AKRESULT in_eLoadResult, uint in_memPoolId, object in_Cookie)
		{
			Lock();
			var handle = (AsyncBankHandle)in_Cookie;
			var callback = handle.bankCallback;
			if (in_eLoadResult != AKRESULT.AK_Success)
			{
				handle.LogLoadResult(in_eLoadResult);
				m_BankHandles.Remove(handle.bankName);
			}
			Unlock();

			if (callback != null)
				callback(in_bankID, in_pInMemoryBankPtr, in_eLoadResult, in_memPoolId, null);
		}

		/// Loads a bank.  This version returns right away and loads in background. See AK::SoundEngine::LoadBank for more information
		public override AKRESULT DoLoadBank()
		{
			return AkSoundEngine.LoadBank(bankName, GlobalBankCallback, this, AkSoundEngine.AK_DEFAULT_POOL_ID, out m_BankID);
		}
	}

	static Dictionary<string, BankHandle> m_BankHandles = new Dictionary<string, BankHandle>();
	static List<BankHandle> BanksToUnload = new List<BankHandle>();
	static System.Threading.Mutex m_Mutex = new System.Threading.Mutex();
	public static bool ThreadSafe = false;
	public static float LastUnloadTime = 0;	//每帧最多加载几个bank
	public static float DelayUnloadSeconds = 30.0f;	//延迟多少时间卸载bank

	public static void PerframeUpdate()
	{
        if(BanksToUnload.Count > 0)
		{
			for(int i = BanksToUnload.Count-1; i >= 0; i--)
			{
				if(Time.time - BanksToUnload[i].BeginUnloadTime > DelayUnloadSeconds)
				{
					m_BankHandles.Remove(BanksToUnload[i].bankName);
					BanksToUnload[i].UnloadBank();
					BanksToUnload.RemoveAt(i);
				}
			}
		}
	}

	public static void Reset()
	{
		m_BankHandles.Clear();
		BanksToUnload.Clear();
	}

	/// Loads a SoundBank. This version blocks until the bank is loaded. See AK::SoundEngine::LoadBank for more information.
	public static void LoadBank(string name)
	{
		Lock();
		BankHandle handle = null;
		if (!m_BankHandles.TryGetValue(name, out handle))
		{
			handle = new BankHandle(name);
			m_BankHandles.Add(name, handle);
			Unlock();
			handle.LoadBank();
		}
		else
		{
			// Bank already loaded, increment its ref count.
			handle.IncRef();
			RemoveFromUnload(handle);
			Unlock();
		}
	}

	/// Loads a SoundBank. This version returns right away and loads in background. See AK::SoundEngine::LoadBank for more information.
	public static void LoadBankAsync(string name, AkCallbackManager.BankCallback callback = null)
	{
		Lock();
		BankHandle handle = null;
		if (!m_BankHandles.TryGetValue(name, out handle))
		{
			AsyncBankHandle asyncHandle = new AsyncBankHandle(name, callback);
			m_BankHandles.Add(name, asyncHandle);
			Unlock();
			asyncHandle.LoadBank();
		}
		else
		{
			// Bank already loaded, increment its ref count.
			handle.IncRef();
			RemoveFromUnload(handle);
			Unlock();
		}
	}

	/// Unloads a SoundBank. See AK::SoundEngine::UnloadBank for more information.
	public static void UnloadBank(string name)
	{
		Lock();
		BankHandle handle = null;
		if (m_BankHandles.TryGetValue(name, out handle))
		{
			handle.DecRef();
			if (handle.RefCount == 0)
			{
				handle.BeginUnloadTime = Time.time;
				BanksToUnload.Add(handle);
			}	
		}
		Unlock();
	}

	private static void RemoveFromUnload(BankHandle handle)
	{
		if(BanksToUnload.Contains(handle))
		{
			BanksToUnload.Remove(handle);
		}
	}

    public static void ForceUnloadAll()
    {
#if UNITY_EDITOR
        foreach(var i in m_BankHandles)
        {
            BanksToUnload.Add(i.Value);
        }
        m_BankHandles.Clear();

        foreach(var i in BanksToUnload)
		{
			i.UnloadBank();
		}
		BanksToUnload.Clear();
#endif
    }

	private static void Lock()
	{
		if(ThreadSafe)
		{
			m_Mutex.WaitOne();
		}
	}

	private static void Unlock()
	{
		if(ThreadSafe)
		{
			m_Mutex.ReleaseMutex();
		}
	}
}
#endif // #if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.